#include "ssd1306.h"
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "font.h"
#include "string.h"

static const char *TAG = "SSD1306";
static i2c_master_dev_handle_t ssd1306_dev_handle = NULL;

// 显示缓冲区
static uint8_t ssd1306_buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8];
static uint8_t ssd1306_buffer_prev[SSD1306_WIDTH * SSD1306_HEIGHT / 8];

// 屏幕状态
static uint8_t ssd1306_init_done = 0;
static uint8_t ssd1306_current_x = 0;
static uint8_t ssd1306_current_y = 0;
static uint8_t ssd1306_reverse_flag = 0; // 反显，0为正常，1为反显
static uint8_t ssd1306_turn_flag = 0;    // 翻转，0为正常，1为翻转

// 某些状态
// 反显 屏幕旋转180度

// 初始化SSD1306 OLED
esp_err_t ssd1306_init(void)
{
    i2c_master_bus_config_t bus_conf = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .i2c_port = I2C_NUM_0,
        .sda_io_num = 21,
        .scl_io_num = 22,
        .flags.enable_internal_pullup = true,
    };

    i2c_master_bus_handle_t bus_handle;
    esp_err_t ret = i2c_new_master_bus(&bus_conf, &bus_handle);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to create I2C bus");
        return ret;
    }

    return ssd1306_init_with_bus(bus_handle);
}

// 使用已有I2C总线初始化SSD1306 OLED
esp_err_t ssd1306_init_with_bus(i2c_master_bus_handle_t bus_handle)
{
    // 配置SSD1306设备
    i2c_device_config_t dev_conf = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
        .device_address = SSD1306_I2C_ADDR,
        .scl_speed_hz = 100000, // 设置I2C频率为400kHz
    };

    if (ssd1306_dev_handle != NULL)
    {
        ESP_LOGW(TAG, "device handle already exists, deleting...");
        i2c_master_bus_rm_device(ssd1306_dev_handle);
        ssd1306_dev_handle = NULL;
    }

    esp_err_t ret = i2c_master_bus_add_device(bus_handle, &dev_conf, &ssd1306_dev_handle);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to add SSD1306 device to existing bus");
        return ret;
    }

    ESP_LOGI(TAG, "SSD1306 init_with_bus, addr: 0x%02X", SSD1306_I2C_ADDR);

    ret = i2c_master_probe(bus_handle, SSD1306_I2C_ADDR, pdTICKS_TO_MS(100));
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Device not found at 0x%02X", SSD1306_I2C_ADDR);
        return ret;
    }

    // 发送初始化命令
    uint8_t init_sequence[] = {
        0xAE, // 关闭显示
        0xD5, // 设置显示时钟分频/振荡频率
        0x80, // 默认值
        0xA8, // 设置多路复用率
        0x3F, // 1/64 duty
        0xD3, // 设置显示偏移
        0x00, // 无偏移
        0x40, // 设置显示开始行
        0x8D, // 电荷泵设置
        0x14, // 开启电荷泵
        0x20, // 设置内存寻址模式
        0x00, // 水平寻址模式
        0xA1, // 段重映射
        0xC8, // COM扫描方向
        0xDA, // 设置COM引脚硬件配置
        0x12, // 默认值
        0x81, // 设置对比度控制
        0xCF, // 中等亮度
        0xD9, // 设置预充电周期
        0xF1, // 默认值
        0xDB, // 设置VCOMH取消选择级别
        0x40, // 默认值
        0xA4, // 恢复RAM内容显示
        0xA6, // 设置正常显示模式
        0xAF, // 开启显示
    };

    for (int i = 0; i < sizeof(init_sequence); i++)
    {
        uint8_t cmd = init_sequence[i];

        int retries = 3; // 重试次数
        do
        {
            ret = ssd1306_write_command(cmd);
            if (ret == ESP_OK)
            {
                break;
            }
        } while (--retries > 0);

        if (ret != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to send command 0x%02X after retries: %s", cmd, esp_err_to_name(ret));
            return ret;
        }

        // 某些命令后添加延时
        if (cmd == 0xAE || cmd == 0x8D || cmd == 0xAF)
        {
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }

    ssd1306_clear();

    ssd1306_current_x = 0;
    ssd1306_current_y = 0;

    ssd1306_init_done = 1;

    ssd1306_update();

    return ESP_OK;
}

// 设置光标位置
void ssd1306_set_cursor(uint8_t x, uint8_t y)
{
    ssd1306_current_x = x;
    ssd1306_current_y = y;
}

void ssd1306_reverse(uint8_t reverse)
{
    ssd1306_reverse_flag |= reverse;
}

void ssd1306_turn(uint8_t turn)
{
    ssd1306_turn_flag |= turn;
}

// 写入字符串
void ssd1306_write_string(const char *str, uint8_t x, uint8_t y, uint8_t size, uint8_t color)
{
    while (*str)
    {
        // 获取字符索引
        uint8_t ch = (uint8_t)*str++;

        if (ch < 32 || ch > 90)
        {
            continue;
        }

        // 计算字符在字体表中的偏移
        uint16_t font_offset = (ch - 97) * 8; // 假设每个字符8字节

        ESP_LOGI(TAG, "Character: %c, Font Offset: %d", ch, font_offset);

        // 每个字符宽度为8列（假设字体为 8x8 或类似）
        for (uint8_t i = 0; i < 8; i++)
        {
            uint8_t data = asc2_1608[0][i];
            ESP_LOGI(TAG, "...Data: 0x%02X", data);
            if (color == 1)
            {
                data = ~data; // 反色显示
            }

            // 计算缓冲区中的索引
            uint16_t buffer_index = ssd1306_current_x + i +
                                    ((ssd1306_current_y / 8) * SSD1306_WIDTH);

            // 确保索引在有效范围内
            if (buffer_index < sizeof(ssd1306_buffer))
            {
                // 将像素数据写入缓冲区
                ssd1306_buffer[buffer_index] = data;
            }
        }

        // 更新当前位置
        ssd1306_current_x += 8;
        // 检查是否需要换行
        if (ssd1306_current_x >= SSD1306_WIDTH)
        {
            ssd1306_current_x = 0;
            ssd1306_current_y += 8;

            // 检查是否超出屏幕底部
            if (ssd1306_current_y >= SSD1306_HEIGHT)
            {
                ssd1306_current_y = 0;
            }
        }
    }
}

void ssd1306_write_char(const char ch, uint8_t x, uint8_t y, uint8_t size)
{
    uint8_t ch_index = ch - ' ';
    uint8_t *data = asc2_1608[ch_index];
    uint8_t _size = 16;
    switch (size)
    {
    case 8:
        data = asc2_0806[ch_index];
        _size = 4;
        break;
    case 16:
        data = asc2_1608[ch_index];
        _size = 16;
        break;
    case 24:
        data = asc2_2412[ch_index];
        _size = 36;
        break;

    default:
        break;
    }

    ESP_LOGI(TAG, "data: %d, size: %d/%d", ch_index, _size, size);
    for (uint8_t i = 0; i < _size; i++)
    {
        uint8_t temp = data[i];
        uint8_t _x = x + i % 8;
        uint8_t p = i / 8;
        for (uint8_t j = 0; j < 8; j++)
        {
            uint8_t _y = j + y + 8 * p;

            // ESP_LOGI(TAG, "x: %d, y: %d, data: %b, p: %d", _x, _y, temp & 0x01, p);

            if (temp & 0x01)
            {
                ssd1306_set_pixel(_x, _y);
            }
            else
            {
                ssd1306_clear_pixel(_x, _y);
            }

            temp >>= 1;
        }
    }
}

void ssd1306_write_chinese(uint8_t index, uint8_t x, uint8_t y, uint8_t size)
{
    uint8_t *data = hzk1[index];
    uint8_t _size = 32;
    switch (size)
    {
    case 16:
        data = hzk1[index];
        _size = 32;
        break;
    case 24:
        data = hzk2[index];
        _size = 72;
        break;
    case 32:
        data = hzk3[index];
        _size = 128;
        break;
    case 40:
        data = hzk4[index];
        _size = 200;
        break;
    default:
        break;
    }

    for (uint8_t i = 0; i < _size; i++)
    {
        uint8_t temp = data[i];
        uint8_t _x = x + i % size;
        // uint8_t p = _size / size - 1 - i / size;
        uint8_t p = i / size;
        for (uint8_t j = 0; j < 8; j++)
        {
            uint8_t _y = j + y + 8 * p;

            if (temp & 0x01)
            {
                ssd1306_set_pixel(_x, _y);
            }
            else
            {
                ssd1306_clear_pixel(_x, _y);
            }

            temp >>= 1;
        }
    }
}

/**
 * @brief 向OLED显示屏写入数据
 *
 * @param data 数据值
 * @return esp_err_t 数据写入成功返回ESP_OK，否则返回错误码
 */
esp_err_t ssd1306_write_data(uint8_t data)
{
    uint8_t buffer[] = {0x40, data};
    uint8_t retries = 3;
    esp_err_t err;
    do {
        err = i2c_master_transmit(ssd1306_dev_handle, buffer, sizeof(buffer), pdMS_TO_TICKS(500));
        if (err == ESP_OK) break;
        vTaskDelay(pdMS_TO_TICKS(50)); // 重试间隔
    } while (--retries > 0);

    return err;
}

/**
 * @brief 向OLED显示屏写入命令
 *
 * @param command 命令值
 * @return esp_err_t 命令写入成功返回ESP_OK，否则返回错误码
 */
esp_err_t ssd1306_write_command(uint8_t cmd)
{
    uint8_t buffer[] = {0x00, cmd};
    uint8_t retries = 3;
    esp_err_t err;
    do {
        err = i2c_master_transmit(ssd1306_dev_handle, buffer, sizeof(buffer), pdMS_TO_TICKS(500));
        if (err == ESP_OK) break;
        vTaskDelay(pdMS_TO_TICKS(50)); // 重试间隔
    } while (--retries > 0);

    return err;
}

/**
 * @brief 清空屏幕
 */
void ssd1306_clear(void)
{
    for (uint32_t i = 0; i < sizeof(ssd1306_buffer); i++)
    {
        ssd1306_buffer[i] = 0;
    }
}

esp_err_t ssd1306_update(void)
{
    esp_err_t err;

    if (!ssd1306_init_done)
    {
        ESP_LOGE(TAG, "SSD1306 not initialized");
        return ESP_ERR_INVALID_STATE;
    }

    if (memcmp(ssd1306_buffer, ssd1306_buffer_prev, sizeof(ssd1306_buffer)))
    {
        ESP_LOGI(TAG, "buffer not changed");
        // return ESP_OK;
    }

    ESP_LOGI(TAG, "PREPARING TO UPDATE SCREEN...");

    static uint8_t buffer[SSD1306_WIDTH + 1];

    ssd1306_write_command(ssd1306_reverse_flag == 1 ? SSD1306_CMD_INVERTDISPLAY : SSD1306_CMD_NORMALDISPLAY);
    // 屏幕翻转
    // if(ssd1306_turn_flag == 1) {
    //     ssd1306_write_command(SSD1306_CMD_COMSCANINC);
    //     ssd1306_write_command(SSD1306_CMD_SEGREMAP);
    // } else {
    //     ssd1306_write_command(SSD1306_CMD_COMSCANDEC);
    //     ssd1306_write_command(SSD1306_CMD_SEGREMAP_INV);
    // }

    // 发送缓冲区数据
    for (uint8_t page = 0; page < 8; page++)
    {
        // 设置列地址范围为 0~127
        err = ssd1306_write_command(SSD1306_CMD_COLUMNADDR); // 0x21
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to write command 设置列地址范围");
            return err;
        }
        err = ssd1306_write_command(0x00); // 起始列地址低 4 位
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to write command 起始列地址低");
            return err;
        }
        err = ssd1306_write_command(0x7F); // 结束列地址（128 列）
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to write command 结束列地址");
            return err;
        }

        // 设置页地址
        err = ssd1306_write_command(SSD1306_CMD_SETPAGEADDR | page); // 设置当前页号
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "Failed to write command 设置当前页号");
            return err;
        }

        // 准备数据缓冲区
        buffer[0] = 0x40; // 数据起始标记

        for (uint8_t column = 0; column < SSD1306_WIDTH; column++)
        {
            buffer[column + 1] = ssd1306_buffer[page * SSD1306_WIDTH + column];
        }

        // 发送一页数据
        err = i2c_master_transmit(ssd1306_dev_handle, buffer, sizeof(buffer), pdMS_TO_TICKS(500));
        if (err != ESP_OK)
        {
            ESP_LOGE(TAG, "==>Failed to send buffer: %s", esp_err_to_name(err));
            return err;
        }
    }

    ESP_LOGI(TAG, "Update screen success");

    memcpy(ssd1306_buffer_prev, ssd1306_buffer, sizeof(ssd1306_buffer));

    return ESP_OK;
}

void ssd1306_set_pixel(uint8_t x, uint8_t y)
{
    if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
        return;

    uint8_t index = ssd1306_index(x, y);
    ssd1306_buffer[index] |= ssd1306_bitmask(y);
}

void ssd1306_clear_pixel(uint8_t x, uint8_t y)
{
    if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
    {
        return;
    }
    uint8_t index = ssd1306_index(x, y);
    ssd1306_buffer[index] &= ~ssd1306_bitmask(y);
}

uint8_t ssd1306_get_pixel(uint8_t x, uint8_t y)
{
    if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
    {
        return 0;
    }
    uint8_t index = ssd1306_index(x, y);
    return ssd1306_buffer[index] & ssd1306_bitmask(y);
}

void ssd1306_draw_hline(uint8_t x1, uint8_t x2, uint8_t y)
{
    for (uint8_t x = x1; x <= x2; x++)
    {
        ssd1306_set_pixel(x, y);
    }
}

void ssd1306_draw_vline(uint8_t x, uint8_t y1, uint8_t y2)
{
    for (uint8_t y = y1; y <= y2; y++)
    {
        ssd1306_set_pixel(x, y);
    }
}

void ssd1306_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
    ssd1306_draw_hline(x1, x2, y1);
    ssd1306_draw_hline(x1, x2, y2);
    ssd1306_draw_vline(x1, y1, y2);
    ssd1306_draw_vline(x2, y1, y2);
    ESP_LOGI(TAG, "Draw rect success x1 = %d, x2 = %d, y1 = %d, y2 = %d", x1, x2, y1, y2);
}

void ssd1306_fill_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
    for (uint8_t x = x1; x <= x2; x++)
    {
        for (uint8_t y = y1; y <= y2; y++)
        {
            ssd1306_set_pixel(x, y);
        }
    }
}